﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using BackTask.Core.Util;
using BackTask.Model;
using Quartz;

namespace BackTask.Core.Extensions
{
    public static class TaskSchedulerExtensions
    {
        private static readonly Dictionary<SimpleTaskJobIntervalTypes, Action<SimpleTaskExecuteConfig, SimpleScheduleBuilder>> ExecuteStrategies = new Dictionary<SimpleTaskJobIntervalTypes, Action<SimpleTaskExecuteConfig, SimpleScheduleBuilder>>()
        {
            { SimpleTaskJobIntervalTypes.Second, (config, builder) => builder.WithInterval(TimeSpan.FromSeconds(config.Interval)) },
            { SimpleTaskJobIntervalTypes.Minute, (config, builder) => builder.WithInterval(TimeSpan.FromMinutes(config.Interval)) },
            { SimpleTaskJobIntervalTypes.Hour, (config, builder) => builder.WithInterval(TimeSpan.FromHours(config.Interval)) },
            { SimpleTaskJobIntervalTypes.Day, (config, builder) => builder.WithInterval(TimeSpan.FromDays(config.Interval)) },
        };

        public static IScheduler AddTaskJob(this IScheduler scheduler, TaskJob job)
        {
            if (JobHelper.IsCronJob(job.GetType()))
            {
                CronTaskJob cronTaskJob = job as CronTaskJob;
                AddCronTaskJob(scheduler, cronTaskJob);
            }
            else if (JobHelper.IsSimpleJob(job.GetType()))
            {
                SimpleTaskJob simpleTaskJob = job as SimpleTaskJob;
                AddSimpleTaskJob(scheduler, simpleTaskJob);
            }

            return scheduler;
        }

        private static async void AddSimpleTaskJob(this IScheduler scheduler, SimpleTaskJob job)
        {
            var config = job.ExcuteConfig;
            TriggerBuilder builder = TriggerBuilder
                                .Create()
                                .WithIdentity(new TriggerKey(job.Name, job.GroupName))
                                .WithDescription(job.Description)
                                .WithSimpleSchedule(builder =>
                                {
                                    if (config.RepeatForever)
                                    {
                                        builder.RepeatForever();
                                    }
                                    else
                                    {
                                        builder.WithRepeatCount(config.ExecuteCount);
                                    }
                                    
                                    builder.WithMisfireHandlingInstructionNextWithRemainingCount();
                                    ExecuteStrategies[job.ExcuteConfig.IntervalType].Invoke(job.ExcuteConfig, builder);
                                });

            builder.WithPriority(job.Priority);
            builder.WithDescription(job.Description);
            ITrigger trigger = builder.Build();
            IJobDetail jobDetail = JobBuilder.Create<Job>()
                .WithIdentity(new JobKey(job.Name, job.GroupName))
                .WithDescription(job.Description)
                .UsingJobData("url", job.Url)
                .Build();
            await scheduler.ScheduleJob(jobDetail, trigger);
        }

        public static async void UpdateTaskJob(this IScheduler scheduler, TaskJob job)
        {
            await scheduler.DeleteJob(new JobKey(job.Name, job.GroupName));
            scheduler.AddTaskJob(job);
        }

        public static async void DeleteTaskJob(this IScheduler scheduler, JobKey jobKey)
        {
            await scheduler.DeleteJob(jobKey);
        }

        public static async void PauseTaskJob(this IScheduler scheduler, JobKey jobKey)
        {
            await scheduler.PauseJob(jobKey);
        }

        public static async void ResumeTaskJob(this IScheduler scheduler, JobKey jobKey)
        {
            await scheduler.ResumeJob(jobKey);
        }

        public static async void RescheduleTaskJob(this IScheduler scheduler, JobKey jobKey)
        {
            ITrigger trigger = await scheduler.GetJobTrigger(jobKey);
            await scheduler.RescheduleJob(trigger.Key, trigger);
        }

        public static async Task<ITrigger> GetJobTrigger(this IScheduler scheduler, JobKey jobKey)
        {
            IEnumerable<ITrigger> triggers = await scheduler.GetTriggersOfJob(jobKey);
            ITrigger trigger = triggers.FirstOrDefault();

            return trigger;
        }

        private static async void AddCronTaskJob(this IScheduler scheduler, CronTaskJob job)
        {
            TriggerBuilder builder = TriggerBuilder
                                .Create()
                                .WithIdentity(new TriggerKey(job.Name, job.GroupName))
                                .WithDescription(job.Description)
                                .WithCronSchedule(job.Cron, opt =>
                                {
                                    opt.WithMisfireHandlingInstructionDoNothing();
                                });
            builder.WithPriority(job.Priority);
            builder.WithDescription(job.Description);

            ITrigger trigger = builder.Build();
            IJobDetail jobDetail = JobBuilder.Create<Job>()
                .WithIdentity(new JobKey(job.Name, job.GroupName))
                .UsingJobData("url", job.Url)
                .WithDescription(job.Description)
                .Build();
            await scheduler.ScheduleJob(jobDetail, trigger);
        }
    }

    internal class Job : IJob
    {
        public async Task Execute(IJobExecutionContext context)
        {
            string url = context.JobDetail.JobDataMap.GetString("url");
            Console.WriteLine(url);
            await Task.CompletedTask;
        }
    }
}
